home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Communication / Gazetteer / Source / Process.m < prev    next >
Text File  |  1991-11-09  |  7KB  |  278 lines

  1. /*
  2.  * Process -- manage i/o with simple subprocesses.
  3.  * Considerably tweaked relative of /NextDeveloper/Examples/Subprocess.
  4.  * M. J. Hawley
  5.  * Copyright (c) MIT Media Laboratory
  6.  * mike@media-lab.mit.edu
  7.  */
  8. #import "Process.h"
  9. #import <appkit/nextstd.h>
  10. #import <appkit/Application.h>
  11. #import <appkit/Panel.h>
  12.  
  13. @interface Process(Private)
  14. - childDidExit;
  15. - fdHandler:(int)theFd;
  16. @end
  17.  
  18. static void
  19. showError (const char *s, id delegate){ // ensure errors never get lost
  20.     if (delegate && [delegate respondsTo:@selector(processError:)])
  21.     [delegate perform:@selector(processError:) with:(void *)s];
  22.     else if (NXApp)    // no delegate, but we're running w/in an App
  23.     NXRunAlertPanel(0, s, 0, 0, 0);
  24.     else
  25.     perror(s);
  26. }
  27.  
  28. static void
  29. fdHandler (int fd, id self) { // DPS handler for output from process
  30.     [self fdHandler:fd];
  31. }
  32.  
  33. static void
  34. getptys(int *master, int *slave){ // attempt to setup the ptys
  35.     #define    PTY_TEMPLATE "/dev/pty??"
  36.     #define    PTY_LENGTH 11
  37.     char device[PTY_LENGTH];
  38.     char *block, *num;
  39.     char *blockLoc; // specifies the location of block for the device string
  40.     char *numLoc; // specifies the pty name with the digit ptyxD
  41.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  42.     
  43.     struct sgttyb setp = {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  44.     struct tchars setc = {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  45.     struct ltchars sltc = {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  46.     int    lset = (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  47.     int    setd = NTTYDISC;
  48.     
  49.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  50.     blockLoc = &device[ strlen("/dev/pty") ];
  51.     numLoc = &device[ strlen("/dev/pty?") ];
  52.     msLoc = &device[ strlen("/dev/") ];
  53.     for (block = "pqrs"; *block; block++){
  54.     *blockLoc = *block;
  55.     for (num = "0123456789abcdef"; *num; num++) {
  56.         *numLoc = *num;
  57.         *master = open(device, O_RDWR);
  58.         if (*master >= 0) {
  59.         *msLoc = 't';
  60.         *slave = open(device, O_RDWR);
  61.         if (*slave >= 0) {
  62.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  63.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  64.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  65.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  66.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  67.             return;
  68.         }
  69.         }
  70.     } /* hunting through a bank of ptys */
  71.     } /* hunting through blocks of ptys in all the right places */
  72.     *master = -1;
  73.     *slave = -1;
  74. }
  75.  
  76. static int
  77. iwait(fd, timeout)
  78.         long fd;
  79.         unsigned long timeout;  /* in seconds */
  80. /*
  81.  * Wait until 'fd' is ready for reading, or 'timeout'.
  82.  * Return '>=0' when 'fd' is readable, '0' if timeout, '-1' on error.
  83.  * Example: 'iwait(f,0)' polls a file descriptor
  84.  * without blocking and returns true if it's readable;
  85.  * e.g., 'iwait(0,0)' is true when standard input is contains something.
  86.  */
  87. {
  88.         struct timeval t;
  89.         int readfd = 1<<fd;
  90.  
  91.         t.tv_sec = timeout, t.tv_usec = 0;
  92.         return (int)select(sizeof(int)*8,  &readfd, (int *)0, (int *)0, &t);
  93. }
  94.  
  95. @implementation Process(Private)
  96.  
  97. - childDidExit { // cleanup after a child process exits
  98.     if (childPid) {
  99.     if (from) DPSRemoveFD(from);
  100.     close(from);
  101.     if (fpTo) fclose(fpTo);
  102.     if (fpFrom) fclose(fpFrom);
  103.     fpTo = fpFrom = (FILE *)0;
  104.     childPid=0;    // specify that child is dead
  105.     if (delegate && [delegate respondsTo:@selector(processDone)])
  106.         [delegate perform:@selector(processDone)];
  107.     }
  108.     return self;
  109. }
  110.  
  111. - fdHandler:(int)fd { // DPS handler for output from process
  112.     // if (iwait(1,fd)<=0) return self;
  113.     if (((bufferCount=read(fd,buffer,BUFSIZE-1))<0)||(!bufferCount))
  114.     return [self childDidExit];
  115.     buffer[bufferCount] = '\0';
  116.     if (delegate && [delegate respondsTo:action])
  117.     [delegate perform:action with:(void *)&buffer];
  118.     return self;
  119. }
  120.  
  121. @end
  122.  
  123.  
  124. @implementation Process
  125.  
  126. + new:(char *)process delegate:del {
  127.     self = [Process alloc];
  128.     [self init:process delegate:del andPty:YES andStderr:YES];
  129.     return self;
  130. }
  131.  
  132. + new:(char *)process delegate:del andPty:(BOOL)wantsPty andStderr:(BOOL)wantsStderr {
  133.     self = [Process alloc];
  134.     [self init:process delegate:del andPty:wantsPty andStderr:wantsStderr];
  135.     return self;
  136. }
  137.  
  138. - init:(char *)process delegate:del {
  139.     return [self init:process delegate:del andPty:NO andStderr:NO];
  140. }
  141.  
  142. - init:(char *)process delegate:del andPty:(BOOL)pty andStderr:(BOOL)err {
  143.     // initializes an instance of process and corresponding UNIX process
  144.     int pipeTo[2];
  145.     int pipeFrom[2];
  146.     int    tty, numFds, fd;    // for temporary use
  147.     int processGroup;
  148.     int pidChild;        // needed because childPid does not exist
  149.                 // until process is instantiated
  150.  
  151.     [self setAction:@selector(processOutput:)];
  152.     if (pty){
  153.         tty = open("/dev/tty", O_RDWR);
  154.     getptys(&masterPty,&slavePty);
  155.     if (masterPty <= 0 || slavePty <= 0) {
  156.         showError("Error grabbing ptys for subprocess.", del);
  157.         return self;
  158.     }
  159.     // remove the controlling tty if launched from a shell,
  160.     // but not Workspace;
  161.     // so that we have job control over the parent application in shell
  162.     // and so that subprocesses can be restarted in Workspace
  163.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0)) {
  164.         ioctl(tty, TIOCNOTTY, 0);
  165.         close(tty);
  166.     }
  167.     } else
  168.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0){
  169.     showError("Error starting UNIX pipes to process.", del);
  170.     return self;
  171.     }
  172.     
  173.     switch (pidChild = vfork()){
  174.     case -1:    // error
  175.     showError("Error starting UNIX vfork of process.", del);
  176.     return self;
  177.  
  178.     case 0:    // child
  179.     if (pty) {
  180.         dup2(slavePty, 0);
  181.         dup2(slavePty, 1);
  182.         if (err) dup2(slavePty, 2);
  183.     } else {
  184.         dup2(pipeTo[0], 0);
  185.         dup2(pipeFrom[1], 1);
  186.         if (err) dup2(pipeFrom[1], 2);
  187.     }
  188.     
  189.     numFds = getdtablesize();
  190.     for (fd=3; fd<numFds; fd++)
  191.         close(fd);
  192.  
  193.     processGroup = getpid();
  194.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  195.     setpgrp (0, processGroup);
  196.     
  197.     // execl(process, 0);
  198.     execl("/bin/sh", "sh", "-c", process, 0);
  199.     perror("vfork (child)"); // should never gets here tho
  200.     exit(1);
  201.  
  202.     default:    // parent
  203.     [self setDelegate:del];
  204.     childPid = pidChild;
  205.     if (pty){
  206.         close(slavePty);
  207.         fpTo = fdopen(masterPty, "w");
  208.         from = masterPty;
  209.         fpFrom = fdopen(masterPty, "r");
  210.     } else {
  211.         close(pipeTo[0]);
  212.         close(pipeFrom[1]);
  213.     
  214.         fpTo = fdopen(pipeTo[1], "w");
  215.         from = pipeFrom[0];
  216.         fpFrom = fdopen(pipeFrom[0], "r");
  217.         }
  218.     setbuf(fpTo, NULL);
  219.     setbuf(fpFrom, NULL);
  220.     DPSAddFD(from,(DPSFDProc)fdHandler,(id)self,NX_MODALRESPTHRESHOLD+1);
  221.     // printf("added %d, %d, %d [%d %d]\n",from, self, delegate, pipeTo[1], pipeFrom[0]);
  222.     return self;
  223.     }
  224. }
  225.  
  226. - puts:(char *)s {
  227.     fputs(s, fpTo);
  228.     return self;
  229. }
  230.  
  231.  
  232. static void
  233. _fgets(s,n,f) char *s; int n; FILE *f; {
  234.     int fd = fileno(f);
  235.     while (iwait(fd,3)>0){
  236.         if (read(fd,s,1) != 1){
  237.             *s = '\0';
  238.             return;
  239.         }
  240.         if (*s=='\n' || --n <= 0){
  241.             *++s = '\0';
  242.             return ;
  243.         }
  244.         if (*s != '\r') ++s;
  245.     }
  246.     *++s = '\0';
  247. }
  248.  
  249. - gets:(char *)s :(int)n {
  250.     *s = '\0';
  251. //    _fgets(s,n,fpFrom);
  252.     fgets(s,n,fpFrom);
  253.     return self;
  254. }
  255.  
  256. - terminate:sender{
  257.     if (childPid){
  258.     kill(childPid+1, SIGTERM);
  259.     [self childDidExit];
  260.     }
  261.     return self;
  262. }
  263.  
  264. - setDelegate:anObject {
  265.     delegate = anObject;
  266.     return self;
  267. }
  268.  
  269. - delegate {
  270.     return delegate;
  271. }
  272.  
  273. - setAction:(SEL)theAction {
  274.     action = theAction;
  275.     return self;
  276. }
  277. @end
  278.